fs (File system) 是文件系统模块,用于操作文件和目录。

支持同步 (sync) 或者异步 (async/callback) 调用,其中同步调用会阻塞主线程,异步调用不会阻塞。

以读取文件内容为例 (test.txt)。

sh hello world bye bye

下面分别是 3 种写法,日常开发中常用 同步操作Promise形式异步操作

```js import fs from 'fs'

// 同步读取 const syncData = fs.readFileSync('./test.txt', 'utf-8') console.log('====sync read====') console.log(syncData)

// 回调形式 异步读取 fs.readFile('./test.txt', 'utf-8', (err, callbackData) => { if (!err) { console.log('====callback read====') console.log(callbackData) } })

// promise形式 异步读取 fs.promises.readFile('./test.txt', 'utf-8').then((promiseData) => { console.log('====promise read====') console.log(promiseData) })

// promise 形式还可以是如下写法(常用) // import fs from 'fs/promises' // fs.readFile('./test.txt', 'utf-8').then((promiseData) => { // console.log('====promise read====') // console.log(promiseData) // }) ```

接下来将介绍一些常用 API,为降低理解成本,主要使用同步方法的形式进行讲解和操作演示。

1 文件操作

1.1 读取文件

fs.readFileSync 基础用法如下:

```js import fs from 'fs'

const txtContent = fs.readFileSync('./test.txt', 'utf-8') ```

以二进制形式读取,操作。

```js const buf = fs.readFileSync('./test.txt')

// 打印Buffer大小 console.log(buf.length) // 修改前2个字符 buf.write('gg')

// 输出修改后的内容 console.log(buf.toString()) ```

1.2 写入文件

基础用法如下 fs.writeFileSync

js fs.writeFileSync('./newTest.txt', 'hello world')

写入二进制文件 (读取一个图片,然后输出到一个新的位置)。

```js // 读取一个图片 const imgBuf = fs.readFileSync('./logo.png') console.log('isBuffer', Buffer.isBuffer(imgBuf), 'bufferSize', imgBuf.length)

// 写入到新文件 fs.writeFileSync('newLogo.png', imgBuf, 'binary') ```

1.3 获取文件信息

通过 fs.statSync 获取文件或者目录的基本信息。

```js import fs from 'fs'

console.log(fs.statSync('./test.txt')) console.log(fs.statSync('./test-dir')) ```

返回的对象属性如下。

常用字段的意义如下。

| 属性名 | 描述 | | ----------- | ---------------------------------------------------- | | dev | 设备 ID,表示该文件所在的设备。 | | mode | 文件权限,包括读、写、执行等权限。 | | nlink | 硬链接数。 | | uid | 用户 ID,表示该文件所属的用户。 | | gid | 组 ID,表示该文件所属的组。 | | rdev | 设备类型,表示该文件所属设备的类型。 | | blksize | 块大小,表示该文件所属设备的块大小。 | | ino | inode 号,表示该文件的 inode 编号。 | | size | 该文件的大小,以字节为单位。 | | blocks | 该文件占用的总块数。 | | atimeMs | 最后访问时间,以毫秒为单位。 | | mtimeMs | 最后修改时间,以毫秒为单位。 | | ctimeMs | 最后状态改变时间,以毫秒为单位。 | | birthtimeMs | 创建时间,以毫秒为单位。 | | atime | 最后访问时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | mtime | 最后修改时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | ctime | 最后状态改变时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | birthtime | 创建时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 |

返回的对象上还包含可直接调用的方案,用于判断文件类型。

```js const fileInfo = fs.statSync('./test.txt') // 判断是文件还是目录 console.log(fileInfo.isFile(), fileInfo.isDirectory())

const dirInfo = fs.statSync('./test-dir') // 判断是文件还是目录 console.log(dirInfo.isFile(), dirInfo.isDirectory())

try { // 查询一个不存在的文件/目录信息(会抛出异常,需要自行捕获) fs.statSync('not_exist.txt') } catch (e) { console.log('文件不存在') } ```

1.4 追加输出

使用 fs.appendFileSync 向文件末尾追加写入内容。

```js // 引入文件系统模块 import fs from 'fs';

// 使用 fs.appendFileSync() 方法向指定文件追加内容 // 参数1:指定文件路径 // 参数2:要追加的内容 fs.appendFileSync('test.txt', 'Hello World2!'); ```

1.5 移动/重命名文件

fs.renameSync 方法用于文件移动,当然也可以是重命名文件。

下面是文件重命名示例。

```js import fs from 'fs'

fs.renameSync('test.txt', 'test2.txt') ```

下面是移动文件示例。

js fs.renameSync('test2.txt', 'test-dir/test2.txt')

1.6 删除文件

fs.unlinkSyncfs.rmSync 都可用于单文件删除。

```js import fs from 'fs'

fs.unlinkSync('test-dir/test2.txt') // fs.rmSync('test-dir/test2.txt') ```

当然后者还支持删除目录,递归删除子文件/目录等。

js // 删除 test-dir 目录(包含其子文件) fs.rmSync('test-dir', { recursive: true })

2 目录操作

2.1 读取目录所有文件

通过 fs.readdirSync 获取目录下的文件信息。

```js import fs from 'fs'

const files = fs.readdirSync('test-dir')

console.log(files) ```

默认情况下只会返回名称。

可通过指定第二个参数 withFileTypes:true 使返回结果包含类型。

js const files2 = fs.readdirSync('test-dir', { withFileTypes: true }) console.log(files2.map((f) => ({ name: f.name, isDirectory: f.isDirectory() })))

2.2 创建目录

使用 fs.mkdirSync 创建目录,可通过设置 recursive:true 来递归创建多级目录。

js fs.mkdirSync('test-dir/a/b/c/d', { recursive: true })

2.3 删除目录

可以使用 fs.rmdirSync 删除目标目录,recursive: true 表明删除包含其子目录。

js fs.rmdirSync('test-dir/a', { recursive: true })

也可以使用上面提到的 fs.rmSync

js fs.rmSync('test-dir/a', { recursive: true })

2.4 监听目录变更

利用 fs.watch 即可实现。

js import fs from 'fs' // 监听当前目录下所有的文件和子目录中的文件 fs.watch('./', { recursive: true }, (eventType, filename) => { console.log(`File '${filename}' has changed: ${eventType}`) })

3 实践

上面主要阐述了日常开发中常用到的一些方法,下面主要介绍 1 个实际开发中常用的例子。

获取指定目录下所有文件的绝对路径

利用 fs.readdirSyncpath.resolve 即可实现。

```js import fs from 'fs' import path from 'path'

function getAllFiles(dirPath, arrayOfFiles) { const files = fs.readdirSync(dirPath, { withFileTypes: true })

arrayOfFiles = arrayOfFiles || []

files.forEach((file) => { if (file.isDirectory()) { arrayOfFiles = getAllFiles(path.resolve(dirPath, file.name), arrayOfFiles) } else { arrayOfFiles.push(path.resolve(dirPath, file.name)) } })

return arrayOfFiles }

// 获取当前目录下所有文件 console.log(getAllFiles('./')) ```

下面是运行结果。

小结

本小节先简单介绍了三种调用文件系统 API 的方式:

日常使用中推荐使用 fs/promise 的方式。

紧接着分别介绍了文件和目录的常规的 CRUD 方法,

最后综合运用上面介绍的方法,解决一个常见场景问题获取指定目录下所有文件的绝对路径